# Copyright (C) 2011 Red Hat, Inc. All rights reserved.
# This file is part of LVM2.

# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU Lesser General Public License v.2.1.

# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Author(s):
#	Jonathan Brassow <jbrassow@redhat.com>
#
# Copy this file to ~/.gdbinit or <working_dir>/.gdbinit

printf "\n\n"
printf "Loading commands:\n"
printf " - dm_list_size <list ptr>\n"
printf " - pv_dev_name <PV ptr>\n"
printf " - first_seg <LV ptr>\n"
printf " - lv_status <LV ptr>\n"
printf " - lv_status_r <LV ptr>\n"
printf " - lv_is_mirrored <LV ptr>\n"
printf " - seg_item <seg ptr> <index>\n"
printf " - seg_status <seg ptr>\n"
printf " - segs_using_this_lv <seg ptr>\n"
printf " - seg_pvs <list ptr>\n"
printf " - \n"
printf "Use 'help <command>' for more info\n"
printf "\n\n"
printf "Popular breakpoints:\n"
printf "break _alloc_image_components\n"
printf "run --repair --use-policies vg/lv\n"
printf "\n\n"

set follow-fork-mode child

# Conventions:
# foo     : function named 'foo' available to user
# __foo   : an internal function
#
# External functions should have a corresponding 'document'
# section.  Internal functions should have leading comments



define dm_list_size
	set $_DLS_list_head = (struct dm_list *)$arg0
	set $_DLS_list = $_DLS_list_head->n
	set $_DLS_size = 0
	
	while (($_DLS_list != $_DLS_list_head) && ($_DLS_size < 100))
		set $_DLS_list = $_DLS_list->n
		set $_DLS_size++
	end

	printf "%d list items\n", $_DLS_size
end

document dm_list_size
Returns the number of elements in the dm_list

	Usage: dm_list_size <list ptr>
end

define pv_dev_name
	set $_PDN_pv = (struct physical_volume *)$arg0
	set $_PDN_dev = $_PDN_pv->dev
	set $_PDN_strl = (struct str_list *)$_PDN_dev->aliases.n

	printf "%s\n", $_PDN_strl->str
end

document pv_dev_name
Print the name of the PV for the given PV pointer

	Usage: pv_dev_name <PV ptr>
end

define seg_pvs
	set $_SP_list_head = (struct dm_list *)$arg0
	set $_SP_list = $_SP_list_head->n
	
	while (($_SP_list != $_SP_list_head) && ($_SP_size < 100))
		set $_SP_spv = (struct seg_pvs *)$_SP_list

		printf "* Can't print PV list\n"

		set $_SP_list = $_SP_list->n
	end

	printf "%d list items\n", $_SP_size
end

document seg_pvs
Print the elements of a seg_pvs list

	Usage: seg_pvs <list ptr>
end

#
# __first_seg <return> <LV>
define __first_seg
	set $arg0 = 0x0
	set $_FS_lv  = (struct logical_volume *)$arg1

	if ($_FS_lv->segments.n != &$_FS_lv->segments)
		set $arg0 = (struct lv_segment *)$_FS_lv->segments.n
	end
end

define first_seg
	set $_seg = 0
	set $_lv=(struct logical_volume *)$arg0

	__first_seg $_seg $_lv

	if ($_seg)
		p $_seg
	else
		printf "No segments (list empty)\n"
	end
end

document first_seg
Returns the pointer to the first segment of an LV

	Usage: first_seg <LV ptr>

WARNING: If the list pointer in 'struct lv_segment' moves,
	 this function will be wrong.
end

#
# __seg_type <return> <seg> <index>
define __seg_type
	set $arg0  = 0x0
	set $_ST_seg  = (struct lv_segment *)$arg1
	set $_ST_index= $arg2
	set $_ST_area = $_ST_seg->areas[$_ST_index]
	set $_ST_type = $_ST_area.type

	set $arg0 = $_ST_type
end

#
# __seg_item <return> <seg> <index>
define __seg_item
	set $arg0  = 0x0
	set $_SI_seg  = (struct lv_segment *)$arg1
	set $_SI_index= $arg2

	if ($_SI_index < $_SI_seg->area_count)
		set $_SI_area = $_SI_seg->areas[$_SI_index]
		set $_SI_type = $_SI_area.type

		if ($_SI_type == AREA_PV)
			set $arg0 = $_SI_area.u.pv.pvseg->pv
		else
			if ($_SI_type == AREA_LV)
				set $arg0 = $_SI_area.u.lv.lv
			end
		end
	end
end

#
# __seg_metaitem <return> <seg> <index>
define __seg_metaitem
	set $arg0  = 0x0
	set $_SMI_seg  = (struct lv_segment *)$arg1
	set $_SMI_index= $arg2

	if (($_SMI_index < $_SMI_seg->area_count) && $_SMI_seg->meta_areas)
		set $_SMI_area = $_SMI_seg->meta_areas[$_SMI_index]
		set $_SMI_type = $_SMI_area.type

		if ($_SMI_type == AREA_PV)
			set $arg0 = $_SMI_area.u.pv.pvseg->pv
		else
			if ($_SMI_type == AREA_LV)
				set $arg0 = $_SMI_area.u.lv.lv
			end
		end
	end
end

define seg_item
	set $_item = 0x0

	__seg_item $_item $arg0 $arg1	
	if ($_item)
		p $_item
	else
		printf "AREA_UNASSIGNED or invalid\n"
	end
end

define seg_metaitem
	set $_metaitem = 0x0

	__seg_metaitem $_metaitem $arg0 $arg1	
	if ($_metaitem)
		p $_metaitem
	else
		printf "AREA_UNASSIGNED or invalid\n"
	end
end

document seg_item
Returns the pointer to the LV or PV for the indexed area of a segment

	Usage: seg_item <struct lv_segment *> <index>

Example - Getting to the sub-lv of a mirror:
	(gdb) p lv->name
	$1 = 0x712548 "lv"

	(gdb) first_seg lv
	$2 = (struct lv_segment *) 0x7128b8

	(gdb) seg_item $2 0
	$3 = (struct logical_volume *) 0x712688

	(gdb) p $3->name
	$4 = 0x712770 "lv_mimage_0"
end

define __status
	set $_s_status = $arg0->status

#	Constants defined in metadata-exported.h

#	if ($_s_status & RAID)
	if ($_s_status & 0x0000000100000000LU)
		set $_s_status = $_s_status & ~0x0000000100000000LU
		printf " RAID"
	end
#	if ($_s_status & RAID_META)
	if ($_s_status & 0x0000000200000000LU)
		set $_s_status = $_s_status & ~0x0000000200000000LU
		printf " RAID_META"
	end
#	if ($_s_status & RAID_IMAGE)
	if ($_s_status & 0x0000000400000000LU)
		set $_s_status = $_s_status & ~0x0000000400000000LU
		printf " RAID_IMAGE"
	end
#	if ($_s_status & MIRRORED)
	if ($_s_status & 0x00008000U)
		set $_s_status = $_s_status & ~0x00008000U
		printf " MIRRORED"
	end
#	if ($_s_status & MIRROR_LOG)
	if ($_s_status & 0x00020000U)
		set $_s_status = $_s_status & ~0x00020000U
		printf " MIRROR_LOG"
	end
#	if ($_s_status & MIRROR_IMAGE)
	if ($_s_status & 0x00040000U)
		set $_s_status = $_s_status & ~0x00040000U
		printf " MIRROR_IMAGE"
	end
#	if ($_s_status & VISIBLE_LV)
	if ($_s_status & 0x00000040U)
		printf " VISIBLE_LV"
		set $_s_status = $_s_status & ~0x00000040U
	else
		printf " *HIDDEN_LV*"
	end
#	if ($_s_status & FIXED_MINOR)
	if ($_s_status & 0x00000080U)
		set $_s_status = $_s_status & ~0x00000080U
		printf " FIXED_MINOR"
	end
#	if ($_s_status & LVM_READ)
	if ($_s_status & 0x00000100U)
		set $_s_status = $_s_status & ~0x00000100U
		printf " LVM_READ"
	end
#	if ($_s_status & LVM_WRITE)
	if ($_s_status & 0x00000200U)
		set $_s_status = $_s_status & ~0x00000200U
		printf " LVM_WRITE"
	end
#	if ($_s_status & SNAPSHOT)
	if ($_s_status & 0x00001000U)
		set $_s_status = $_s_status & ~0x00001000U
		printf " SNAPSHOT"
	end
#	if ($_s_status & PVMOVE)
	if ($_s_status & 0x00002000U)
		set $_s_status = $_s_status & ~0x00002000U
		printf " PVMOVE"
	end
#	if ($_s_status & LOCKED)
	if ($_s_status & 0x00004000U)
		set $_s_status = $_s_status & ~0x00004000U
		printf " LOCKED"
	end
#	if ($_s_status & LV_NOTSYNCED)
	if ($_s_status & 0x00080000U)
		set $_s_status = $_s_status & ~0x00080000U
		printf " LV_NOTSYNCED"
	end
#	if ($_s_status & CONVERTING)
	if ($_s_status & 0x00400000U)
		set $_s_status = $_s_status & ~0x00400000U
		printf " CONVERTING"
	end
#	if ($_s_status & LV_REBUILD)
	if ($_s_status & 0x100000U)
		set $_s_status = $_s_status & ~0x100000U
		printf " LV_REBUILD"
	end
#	if ($_s_status & PARTIAL_LV)
	if ($_s_status & 0x1000000U)
		set $_s_status = $_s_status & ~0x1000000U
		printf " PARTIAL_LV"
	end
#	if ($_s_status & MERGING)
	if ($_s_status & 0x10000000U)
		set $_s_status = $_s_status & ~0x10000000U
		printf " MERGING"
	end
#	if ($_s_status & LV_WRITEMOSTLY)
	if ($_s_status & 0x10000000000U)
		set $_s_status = $_s_status & ~0x10000000000U
		printf " LV_WRITEMOSTLY"
	end

	if ($_s_status)
		printf " 0x%x", $_s_status
	end
end

#
# __print_indent <num indents> [No marks]
define __print_indent
	set $_PI_indent = $arg0
	set $_PI_lead_mark = 0

	while ($_PI_indent)
		if ($_PI_indent == 1)
			if ($argc > 1)
				if ($_PI_lead_mark)
					printf "        "
				else
					printf "|       "
				end
			else
				printf "|-----> "
			end
		else
			printf "|       "
			set $_PI_lead_mark = 1
		end
		set $_PI_indent--
	end
end

define lv_status
	# Use __lv because we don't want to overwrite higher functions
	set $__lv = (struct logical_volume *)$arg0

	if ($argc == 2)
		__print_indent $arg1
	end
	printf "%s->status:", $__lv->name
	__status $__lv
	printf "\n"
end

document lv_status
Display the flags that are set on an LV.

	Usage: lv_status <LV ptr>
end

define seg_status
	set $_seg=(struct lv_segment *)$arg0

	if ($argc == 2)
		__print_indent $arg1 1
	end
	printf "[ (%s) seg->status:", $_seg->lv->name
	__status $_seg
	printf " ]\n"
end

document seg_status
Display the flags that are set on an lv_segment.

        Usage: seg_status <(struct lv_segment *)>
end

#
# get_only_segment_using_this_lv <return> <LV>
define __get_only_segment_using_this_lv
	set $arg0 = 0x0
	set $_lv=(struct logical_volume *)$arg1
	set $_seg_list_head = &$_lv->segs_using_this_lv
	set $_s = $_lv->segs_using_this_lv.n
	set $_i = 0

	while (($_s != $_seg_list_head) && ($_i < 100))
		set $_seg_list = (struct seg_list *)$_s
		set $_seg = (struct lv_segment *)$_seg_list->seg

		set $_i++
		set $_s = $_s->n
	end

	if ($_i > 1)
		printf "More than %s using %s\n", ($_i > 99) ? "100 segments" : "one segment", $_lv->name
	end
	if ($_i == 1)
		set $arg0 = $_seg
	end
end

define segs_using_this_lv
	set $_lv=(struct logical_volume *)$arg0
	set $_seg_list_head = &$_lv->segs_using_this_lv
	set $_s = $_lv->segs_using_this_lv.n
	set $_i = 0

	if ($_s != $_seg_list_head)
		printf "Segments using %s\n", $_lv->name
	else
		printf "No segments using %s\n", $_lv->name
	end
	while ($_s != $_seg_list_head)
		set $_seg_list = (struct seg_list *)$_s
		set $_seg = (struct lv_segment *)$_seg_list->seg
		printf "  %d) seg: %p", $_i, $_seg
		if ($_seg->lv < 0x200)
			printf "  [BAD LV POINTER FROM THIS SEG]\n"
		else
			printf "  [seg found in %s]\n", $_seg->lv->name
		end
		set $_i++
		set $_s = $_s->n
	end
end

document segs_using_this_lv
Display the segments (and their associated LV) using an LV

	Usage: segs_using_this_lv <LV ptr>

Example:
	(gdb) lv_is_mirrored lv
	lv is mirrored ('core' log)

	(gdb) segs_using_this_lv lv
	No segments using lv

	(gdb) first_seg lv
	$1 = (struct lv_segment *) 0x92d360

	(gdb) seg_item $1 0
	$2 = (struct logical_volume *) 0x928f58

	(gdb) segs_using_this_lv $2
	Segments using lv_mimage_0
	  0) seg: 0x92d360  [seg found in lv]
end

#
# __next_area_index <return> <seg> <seg_item>
define __next_area_index
	set $arg0 = 0x0
	set $_seg = (struct lv_segment *)$arg1
	set $_item = 0x0
	set $_i = 0

	__seg_item $_item $_seg $_i
	while ($_item && ($_item != $arg2))
		set $_i++
		__seg_item $_item $_seg $_i
	end

	# $_i points to current, now get next (if there)
	set $_i++
	__seg_item $_item $_seg $_i

	if ($_item)
		set $arg0 = $_i
	end
end

#
# __lv_status_r <LV>
# Decend tree, printing LV and seg status as we go.  This
# performs a depth first approach (but can't come up) 
#
# or
#
# __lv_status_r <sub_lv> <seg using sub_lv>
# Try continuing decent of tree by first shifting to the
# next 'area' in the seg ($arg1).  If no more areas, then
# try going to the next segment.
define __lv_status_r
	if ($argc == 1)
	        set $_lv=(struct logical_volume *)$arg0
		set $_seg_list_head = &$_lv->segments
		set $_s = $_lv->segments.n
		set $_area_index = 0

#		printf "\n"
		lv_status $_lv $indent
	else
		set $_seg = (struct lv_segment *)$arg1

		__next_area_index $_area_index $_seg $arg0

		# Don't fuck this up.  We need the next two lines here.
	        set $_lv=(struct logical_volume *)$_seg->lv
		set $_seg_list_head = &$_lv->segments
		set $_s = (struct dm_list *)$_seg

		if (!$_area_index)
			set $_s = $_s->n
		end
	end

	if ($_s == $_seg_list_head)
		if ($argc == 1)
			__print_indent $indent 1
			printf "[ No segments for %s ]\n", $_lv->name
		end
		__get_only_segment_using_this_lv $_seg $_lv

		if ($_seg && $indent)
			set $indent--
			__lv_status_r $_lv $_seg
		end
	else
		set $_seg = (struct lv_segment *)$_s
		set $_type = 0x0

		if (!$_area_index)
			seg_status $_seg $indent
		end
		__seg_type $_type $_seg $_area_index
		if ($_type == AREA_LV)
			set $indent++

			__seg_metaitem $_lv $_seg $_area_index
			if ($_lv)
				set $rindent = $indent
				set $rseg = $_seg
				set $rarea_index = $_area_index
				set $rlv = $_lv

				__lv_status_r $_lv

				set $indent = $rindent
				set $_seg = $rseg
				set $_area_index = $rarea_index
				set $_lv = $rlv
			end

			__seg_item $_lv $_seg $_area_index
			__lv_status_r $_lv
		else
			if ($_seg->log_lv)
				set $indent++
				set $_log_seg = 0x0

				__first_seg $_log_seg $_seg->log_lv
				lv_status $_seg->log_lv $indent
				seg_status $_log_seg $indent

				set $indent--
			end
			__get_only_segment_using_this_lv $_seg $_lv
			if ($_seg)
				set $indent--
				__lv_status_r $_lv $_seg
			end
		end
	end
end

define lv_status_r
	set $indent = 0
	__lv_status_r $arg0
end

document lv_status_r
Display the status flags of an LV and its sub_lvs.

	Usage: lv_status_r <LV ptr>

This function is useful for checking that all the LVs that
compose a logical volume have the correct flags set (and also
their associated lv_segments)
end

define lv_is_mirrored
	set $_lv=(struct logical_volume *)$arg0
	set $_fs=(struct lv_segment *)$_lv->segments.n
	set $_log_lv=(struct logical_volume *)$_fs->log_lv

#	if ($_lv->status & MIRRORED)
	if ($_lv->status & 0x00008000U)
		printf "%s is mirrored (", $_lv->name
		if ($_log_lv)
			if ($_log_lv->status & 0x00008000U)
				printf "'mirrored' log)\n"
			else
				printf "'disk' log)\n"
			end
		else
			printf "'core' log)\n"
		end
	else
		printf "%s is not mirrored\n", $_lv->name
	end
end

document lv_is_mirrored
Report whether the given LV is mirrored (and its log type).

	Usage: lv_is_mirrored <LV ptr>
end
